﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Net;
using System.Windows;
using System.IO;
using System.Xml;
using System.Collections;
using System.Collections.Specialized;
using System.Threading;

namespace Atlantyda
{
    public class Map
    {
        private int size;

        private Dictionary<String, MapField> fields = new Dictionary<String, MapField>();
        private Dictionary<String, Person> persons = new Dictionary<String, Person>();
        private HttpWebRequest request = null;
        private WebResponse response = null;
        private CookieContainer cookieContainer = new CookieContainer();

        public int Size
        {
            get { return this.size; }
            set { this.size = value; }
        }

        public Dictionary<String, MapField> Fields
        {
            get { return fields; }
        }

        public void Clear()
        {
            foreach (KeyValuePair<string, MapField> field in this.fields)
            {
                field.Value.Panel.Hide();
            }
            this.fields.Clear();
            this.persons.Clear();
            GC.Collect();
            this.cookieContainer = new CookieContainer();
        }

        public void RenderMap()
        {
            if (Agent.Connected)
            {
                if (Agent.MainForm.LoadingBox.Visible) Agent.MainForm.HideLoadingBox();
                int size_field = (Agent.MainForm.AgentPanel.Height - Agent.MainForm.AgentConsole.Height - 20) / Agent.Map.Size;
                foreach (KeyValuePair<string, MapField> field in this.fields.ToList())
                {
                    if (Agent.Connected)
                    {
                        field.Value.Panel.InvokeEx(panel => panel.BackColor = field.Value.Color);
                        field.Value.Panel.InvokeEx(panel => panel.Height = size_field);
                        field.Value.Panel.InvokeEx(panel => panel.Width = size_field);
                        field.Value.Panel.InvokeEx(panel => panel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle);
                        System.Drawing.Point location = new System.Drawing.Point(((field.Value.Panel.Height - 1) * field.Value.CoordianteX) + 10, ((field.Value.Panel.Width - 1) * (Agent.Map.Size - field.Value.CoordianteY - 1)) + 10);
                        field.Value.Panel.InvokeEx(panel => panel.Location = location);
                        if (field.Value.Person != null)
                        {
                            System.Drawing.Graphics GraphicsObject = field.Value.Panel.CreateGraphics();
                            GraphicsObject.DrawImage(field.Value.Person.Image, 0, 0, size_field, size_field);
                            GraphicsObject.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
                        }
                        else
                        {
                            System.Drawing.Graphics GraphicsObject = field.Value.Panel.CreateGraphics();
                            GraphicsObject.Clear(field.Value.Color);
                        }
                    }
                    else
                    {
                        break;
                    }
                }
                if (Agent.MainForm.LoadingBox.Visible && Agent.Connected)
                {
                    Agent.MainForm.LoadingBox.Invoke(new Action(Agent.MainForm.HideLoadingBox));
                }
            }
        }

        public void AddField(int x, int y, int height, Color type, int energy)
        {
            this.fields.Add(x + "_" + y, new MapField(x, y, height, type, energy));
        }

        public void AddField(int x, int y, int height, Color type)
        {
            this.fields.Add(x + "_" + y, new MapField(x, y, height, type, 0));
        }

        public void AddStartField(int height, Color type, int energy)
        {
            this.fields.Add("0_0", new MapField(0, 0, height, type, energy));
        }

        public void GetWorld()
        {
            XmlDocument worldsXml = new XmlDocument();
            worldsXml.LoadXml(this.GetDataFromWebServer("http://" + Agent.WebAddress + "/Service.asmx/GetWorld?name=" + Agent.WorldName));
            XmlElement root = worldsXml.DocumentElement;
            XmlNode sizeXmlNode = root.FirstChild;
            Agent.Map.Size = System.Convert.ToInt32(sizeXmlNode.InnerText, 10);
            XmlNode arXMLNode = root["ar"];
            XmlNodeList aXmlNodeList = arXMLNode.ChildNodes;

            for (int x = 0; x < Agent.Map.Size; x++)
            {
                XmlNodeList fXmlNodeList = aXmlNodeList.Item(x).ChildNodes;
                for (int y = 0; y < Agent.Map.Size; y++)
                {
                    XmlNode fXmlNode = fXmlNodeList.Item(y);
                    int energy = Convert.ToInt32(fXmlNode.Attributes.GetNamedItem("e").Value);
                    Color type = MapField.TYPE_DEFAULT;
                    if (fXmlNode.Attributes.GetNamedItem("o").Value == "1")
                    {
                        type = MapField.TYPE_WALL;
                    }
                    else if (energy != 0)
                    {
                        type = MapField.TYPE_ENERGY;
                    }
                    int height = Convert.ToInt32(fXmlNode.Attributes.GetNamedItem("h").Value);
                    this.AddField(x, y, height, type, energy);
                }
            }
        }

        public void GetUpdate()
        {
            XmlDocument worldsXml = new XmlDocument();
            worldsXml.LoadXml(this.GetDataFromWebServer("http://" + Agent.WebAddress + "/Service.asmx/GetUpdate"));
            XmlElement root = worldsXml.DocumentElement;
            XmlNodeList fXmlNodeList = root.ChildNodes;
            foreach (XmlNode fXmlNode in fXmlNodeList)
            {
                if (Agent.Connected)
                {
                    String x = fXmlNode.Attributes.GetNamedItem("x").InnerText;
                    String y = fXmlNode.Attributes.GetNamedItem("y").InnerText;
                    int energy = Convert.ToInt32(fXmlNode.Attributes.GetNamedItem("e").InnerText);
                    MapField field = this.fields[x + "_" + y];
                    if (field.Energy < energy)
                    {
                        field.Energy = energy;
                        field.Color = MapField.TYPE_ENERGY;
                    }
                    else if (energy == 0)
                    {
                        field.Energy = energy;
                        field.Color = field.GenerateColorByHeight();
                    }

                    if (fXmlNode.FirstChild != null)
                    {
                        XmlNode aXmlNode = fXmlNode.FirstChild;
                        String name = aXmlNode.Attributes.GetNamedItem("n").InnerText;
                        String group_name = aXmlNode.Attributes.GetNamedItem("g").InnerText;
                        energy = Convert.ToInt32(aXmlNode.Attributes.GetNamedItem("e").InnerText);
                        Person person = null;
                        if (!this.persons.TryGetValue(name + "." + group_name, out person))
                        {
                            if (name == Agent.AgentName && group_name == Agent.GroupName)
                            {
                                person = new Person(name, group_name, field.CoordianteX, field.CoordianteY, energy, true);
                            }
                            else
                            {
                                person = new Person(name, group_name, field.CoordianteX, field.CoordianteY, energy);
                            }
                            this.persons.Add(person.Name + "." + person.GroupName, person);
                            field.Person = person;
                            if (name == Agent.AgentName && group_name == Agent.GroupName && !Agent.Height.HasValue)
                            {
                                Agent.Height = field.Height;
                                Agent.RunBot();
                            }
                        }
                        else
                        {
                            MapField oldField = this.fields[person.CoordianteX + "_" + person.CoordianteY];
                            oldField.Person = null;
                            person.Connected = true;
                            person.Energy = energy;
                            person.CoordianteX = field.CoordianteX;
                            person.CoordianteY = field.CoordianteY;
                            field.Person = person;
                        }
                    }
                    else
                    {
                        if (field.Person != null) field.Person.Connected = false;
                        field.Person = null;
                    }
                }
            }
            this.RenderMap();
            this.UpdateAgentList();
        }

        public String GetDataFromWebServer(String url)
        {
            String responseFromServer = "";
            try
            {
                Uri address = new Uri(url);
                this.request = WebRequest.Create(address) as HttpWebRequest;
                this.request.Method = "GET";
                this.request.ContentType = "text/xml";
                this.request.UserAgent = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/18.6.872.0 Safari/535.2 UNTRUSTED/1.0 3gpp-gba UNTRUSTED/1.0";
                this.request.ContentLength = 0;
                this.request.CookieContainer = this.cookieContainer;
                this.response = this.request.GetResponse();
                Stream dataStream = response.GetResponseStream();
                StreamReader reader = new StreamReader(dataStream);
                responseFromServer = reader.ReadToEnd();
                reader.Close();
                response.Close();
            }
            catch (WebException webEx)
            {
                MessageBox.Show(webEx.Message);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            return responseFromServer;
        }

        public void UpdateAgentList()
        {
            Agent.MainForm.AgentList.InvokeEx(list => list.Items.Clear());
            foreach (KeyValuePair<string, Person> agent in Agent.Map.persons)
            {
                if (agent.Value.Connected)
                    Agent.MainForm.AgentList.InvokeEx(list => list.Items.Add(agent.Value.Name + "." + agent.Value.GroupName + " (" + agent.Value.Energy + ")"));
            }
        }
    }
}
